# _*_ coding: utf-8 _*_
#!/usr/bin/env python

#      ___________________
#  ___| Modulbeschreibung \_________________________________________________________________________
# | Der Server verifiziert Verbindungswünsche und verwaltet das Rennen
# |__________________________________________________________________| Zuständigkeit [Jannik |____

import SocketServer
import time
import cPickle
import socket
import threading
import thread
VERSION = '0.0001'



class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):

	def __init__(self, server_address, RequestHandlerClass, maxplayers):
		"""Constructor.  May be extended, do not override."""
		try:
			SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
			#SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass)
			self.socket = socket.socket(self.address_family,
									self.socket_type)
			self.server_bind()
			self.server_activate()
			self.lock=thread.allocate_lock()
			self.maxplayers = maxplayers
			self.playerinf = [None,None,None,None,None,None,None,None,]
			self.playerdata = [None,None,None,None,None,None,None,None]
								
			self.counter = 0
			self.race_in_progress = False
			self.is_running = True
		except:
			self.is_running = False
		
	def startRace(self):
		'''Startet daS Rennen'''
		print "Rennen wird gestartet"
		self.race_in_progress = True
		self.lock.acquire() 
		print "test"
		for i in range(8):
			if self.playerdata[i] != None:
				
				#Fehlerbehandlung wenn nicht gesendet werden kann.
		
				try:
					data = {"race"	: "start"}
					buf = cPickle.dumps(data)
					self.playerdata[i].send(buf)
				
				except socket.error, (value, message):
					self.lock.release() 
					raise socket.error(value, message)
		self.lock.release() 	

	def server_close(self):
		"""Called to clean-up the server.
		May be overridden.	
		"""
		self.is_running = False
		self.socket.close()
		
	def handle_error(self, request, client_address):
		'''Hier koennte eine eigene Fehlerbehandlung hinein'''
		print '-'*40
		print 'Exception happened during processing of request from',
		print client_address
		import traceback
		traceback.print_exc() # XXX But this goes to stderr!
		print '-'*40
			
#Mixin fuer einen threaded server benutzen! in dem fall weahrscheinlich nicht noetig und kann zu fehlern fuehren!
#class GameServer(SocketServer.ThreadingMixIn, SocketServer.BaseRequestHandler)
class GameServer(SocketServer.BaseRequestHandler):

	def __init__(self, request, client_address, server):
		SocketServer.BaseRequestHandler.__init__(self, request, client_address, server)

	def setup(self):
		
		'''Erstellt alle wichtigen Variablen'''

		print self.client_address, 'connected!'
		
		self.connection_successfull = True

		self.name		= 'Unknown'
		self.ip			= self.client_address
		self.number		= None
		self.rounds		= 0
		self.points		= 0
		self.item		= None
		self.latency	= None
		
		self.x_pos, self.y_pos		= 0.0, 0.0
		self.x_speed, self.y_speed	= 0.0, 0.0
		self.angle					= 0.0
		
		

	def handle(self):
		'''Leitet die Spieldaten an alle Spieler weiter'''
		
		#Daten empfangen
		try:
			buf = self.request.recv(1024)
		except socket.error, (value, message):
			self._disconnect()
			raise socket.error(value,message)
		try:
			data = cPickle.loads(buf)
		except pickle.pickleError, (args, message):
			raise pickle.pickleError(args, message)
		self.name = data["name"]
		
		#Wenn das Rennen noch nicht gestartet ist kann verbunden werden wenn nicht bereits 8 Spieler verbunden sind
		if self.server.race_in_progress != True:
			#Spielernummer vergeben
			if self.server.counter < self.server.maxplayers:
				self.server.maxplayers +=1		
				for player in range(8):	
					if self.server.playerinf[player] == None:
						self.server.lock.acquire() 
						self.server.playerdata[player] = self.request 	#Enthält den Socket für jeden Spieler
						self.server.playerinf[player] = [self.name, self.ip]		#lokale Spielerinformationen	
						self.number = player
						
						self.server.counter+=1
						self.server.connection_successfull = True
						self.server.lock.release() 
						break
					else:
						#Wenn kein Platz mehr frei ist wird nichtsmehr gesendet
						self.connection_successfull = False
		else:
			self.server.connection_successfull = False
		
		
		#War der Verbindungsversuch erfolgreich wird die Spielernummer zugewiesen und die Schleife zum weiterleiten aller Daten aktiviert
		if self.server.connection_successfull:
			
			#Daten senden
			data = {"number" 	: self.number,
					"playerinf"	: self.server.playerinf}
			buf = cPickle.dumps(data)
			
			try:
				self.request.send(buf)
			except socket.error, (value, message):
				self._disconnect()
				raise socket.error(value,message,self.number)
				
			print self.ip, "aendert den Namen in", self.name
			self.server.playerinf[self.server.counter-1][0] = self.name
			
			#Spielerinformationen aktualisieren
			self._updatePlayerinf()			


			while self.server.is_running:
				
				#Daten des Client empfangen...
				try:
					buf = self.request.recv(4096)
				except socket.error, (value, message):
					self._disconnect()
					raise socket.error(value, message)
			
				
				self.server.lock.acquire() 

				#... und an alle anderen weiterleiten
				for i in range(8):
					if self.server.playerdata[i] != None and i != self.number:
				
						try:
							self.server.playerdata[i].send(buf)
						
						#Fehlerbehandlung wenn nicht gesendet werden kann.
						except socket.error, (value, message):
							self._disconnect()
							self.server.lock.release() 
							raise socket.error(value, message)
				self.server.lock.release() 
		else:
			#Wenn keine Verbindung aufgebaut werden kann, so wird dies dem Spieler mitgeteilt
			data = {"race" 	: "full"}
			buf = cPickle.dumps(data)
			
			try:
				self.request.send(buf)
			except socket.error, (value, message):
				self._disconnect()
				raise socket.error(value,message,self.number)
			self._disconnect()
			
	def finish(self):
		'''Kuemmert sich um den Verbindungsabbau'''
		self._disconnect()
	
	def _disconnect(self):
		'''Zuständig für den Reibungslosen Verbindungsabbau'''
		#Erst wird der Socket geschlossen...
		self.request.close()
		print self.client_address, 'disconnected!'	
		#...und dann ggf. aufgeräumt	
		if self.server.connection_successfull:
			self.server.playerdata[self.number] = None
			self.server.playerinf[self.number] = None
			self.server.maxplayers -=1	
			self._updatePlayerinf()

	def _updatePlayerinf(self):
		'''Aktualisiert die Spielerinformationen'''
		self.server.lock.acquire() 

		for i in range(8):
			if self.server.playerdata[i] != None and i != self.number:
				
				try:
					data = {"playerinf"	: self.server.playerinf}
					buf = cPickle.dumps(data)
					self.server.playerdata[i].send(buf)
				
				except socket.error, (value, message):
					self.server.lock.release() 
					raise socket.error(value, message)
		self.server.lock.release() 
		
class Start(threading.Thread):
	'''Diese Klasse ist für das Starten des Servers zuständig'''
	def __init__(self,ip, port):
		threading.Thread.__init__(self)
		self.address = (ip,port)
				
	def run(self):
		self.server = ThreadingTCPServer(self.address, GameServer,8)#SocketServer.ThreadingTCPServer(('localhost', 50000), GameServer )
		

		if self.server.is_running:
			print "Server started"
			self.server.serve_forever()
		else: self.stopServer()

	def startRace(self):
		self.server.startRace()
	
	def stopServer(self):
		self.server.server_close()
	
if __name__ == '__main__': 
	#server host is a tuple ('host', port)
	server = ThreadingTCPServer(('192.168.178.20', 50000), GameServer,8)#SocketServer.ThreadingTCPServer(('localhost', 50000), GameServer )
	print "Server started"

	server.serve_forever()

